#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "stack.h" //手撸了一个栈
#include "cal.h" //声明函数原型，从而可以把main写在开头

char* Error_Parameter = "ERROR_01";
char* Error_Format = "ERROR_02";
char* Error_Logic = "ERROR_03";
#define HANDLE_FORMAT_ERROR 0 //格式错误
#define HANDLE_SUCCESS 1      //成功
#define HANDLE_BRACKET_DONE 2 //一个括号处理完毕
#define PARSE_SUCCESS 1
#define PARSE_ERROR 0

stack* num_stack;
stack* sign_stack;

int main(int argc,char ** argv)
{
	if(argc != 2) {
		printf(Error_Parameter);
		return -1;
	}
	num_stack = create_stack();
	sign_stack = create_stack();
	char *exp = argv[1]; //从命令行参数接收表达式
	//用指针遍历表达式，parse_an_element传入exp的指针(二重指针),以自动移动指针
	while(*exp != 0) { 
		if(parse_an_element(&exp) == PARSE_ERROR) { //解析元素，存入对应的栈中，在解析的过程中会进行一些计算
			return -1;
		}
	}
	// 解析完成之后，依次对符号栈里的符号进行运算
	while(!is_empty(sign_stack)) { 
		if(handle_a_sign() != HANDLE_SUCCESS) {
			return -2;
		}
	}
	//所有计算完成后，符号栈应为空，数字栈应只剩一个元素，即计算结果
	if(is_empty(sign_stack) && stack_length(num_stack) == 1) {
		printf("%g",get(num_stack)); //%g可以用来输出有效数字
		return 0;
	} else {
		printf(Error_Format);
		return -1;
	}
}

/**
 * 符号优先级：
 *   0: + -
 *   1: * /
 *   2: ^
 *   3: ( )
 */
int get_priority(char sign)
{
	switch(sign) {
		case '+':
		case '-':
			return 0;
		case '*':
		case '/':
			return 1;
		case '^':
			return 2;
		case '(':
		case ')':
			return 3;
		default:
			return -1;
	}
}

char is_sign(char c)
{
	return get_priority(c) >= 0;
}

char is_num(char c)
{
	return '0' <= c && c <= '9' || c == '.';
}

//根据符号返回a和b计算的结果
double do_calc(char sign,double a,double b)
{
	switch(sign) {
		case '+':
		return a + b;
		case '-':
		return a - b;
		case '*':
		return a * b;
		case '/':
		return a / b;
		case '^':
		return pow(a,b);
	}
}

/**
 * 从符号栈中弹出并处理一个符号
 * 若为(则啥也不干并返回2
 * 若为运算符 则再弹出两个数字并进行计算，
 * 		若计算失败或数字不够了，返回0，成功返回1
 * 若符号用完，返回0
 */
char handle_a_sign()
{
	char sign;
	double a,b;
	if(is_empty(sign_stack)) {//若符号栈为空，则说明格式错误
		printf(Error_Format);
		return HANDLE_FORMAT_ERROR;
	}
	sign = pop(&sign_stack);//若为括号开，说明一个括号处理完毕，返回2以通知上层函数
	if(sign == '('){
		return HANDLE_BRACKET_DONE;
	}
	if(is_empty(num_stack)){//若数字栈为空，则说明格式错误
		printf(Error_Format);
		return HANDLE_FORMAT_ERROR;
	}
	b = pop(&num_stack);//弹出两个数字a和b，注意弹出顺序
	if(is_empty(num_stack)){
		printf(Error_Format);
		return HANDLE_FORMAT_ERROR;
	}
	a = pop(&num_stack);
	if(sign == '/' && b == 0) {
		printf(Error_Logic);
		return HANDLE_FORMAT_ERROR;
	}
	push(&num_stack,do_calc(sign,a,b));//进行计算，并把计算结果入数字栈
	return HANDLE_SUCCESS;
}


/**
 * 从当前字符指针解析一个元素，并作出对应操作，并把字符指针指向下一个元素开头
 * 元素包括：完整的数字、符号
 * 对应操作包括：
 *     若为一个数字，则入数字栈
 *     若为一个符号，则做以下考虑：
 *         若符号栈为空，直接入符号栈
 *         若符号栈非空，则判断符号栈顶的元素
 *         	  若为括号闭，则重复调用handle_a_sign进行计算，直到消除括号开，括号闭不入栈
 *            若优先级高于新解析的元素，或为'('则入栈
 *            否则(即优先级低于新解析的元素),则调用一次handle_a_sign进行计算
 */
char parse_an_element(char** pc)
{
	char* c = *pc; //当前字符的指针
	double num = 0;
	char handling_frac = 0; //是否正在处理小数部分
	char frac_bit = 1; //正在处理的小数部分的位数
	if(!is_num(*c) && !is_sign(*c)) { //判断当前字符，若不是数字也不是符号，直接格式报错
		printf(Error_Format);
		return PARSE_ERROR;
	}
	if(is_sign(*c)) { //若为符号
		if(is_empty(sign_stack)) { //符号栈为空，直接入符号栈
			push(&sign_stack,(double)(*c));
			// printf("sign:");
			// print_stack_c(sign_stack);
			c++;
			*pc = c;
		} else if(*c == ')') { //栈顶元素为括号闭
			while(1) {
				switch(handle_a_sign()){
					case HANDLE_FORMAT_ERROR:
						return PARSE_ERROR;
					case HANDLE_BRACKET_DONE:
						c++;
						*pc = c;
						return PARSE_SUCCESS;
				}
			}		
		} else if(//若栈顶元素优先级高于新解析的元素，或为'('则入栈
			sign_stack->last->value == '(' 
				|| 
			get_priority(*c) > get_priority(get(sign_stack))
		){
			push(&sign_stack,(double)(*c));
			// printf("sign:");
			// print_stack_c(sign_stack);
			c++;
			*pc = c;
		} else{
			if(handle_a_sign()!=1) {
				return PARSE_ERROR;
			}
		}
		return PARSE_SUCCESS;
	}
	while(is_num(*c)) {
		if(*c == '.') {
			handling_frac ++;
		} else if (!handling_frac) {
			num = num * 10 + (*c & 0x0f);
		} else {
			num += (*c & 0x0f) / pow(10,frac_bit);
			frac_bit ++;
		}
		c++;
	}
	*pc = c;
	if(handling_frac < 2) {
		push(&num_stack,num);
		// printf("num:");
		// print_stack(num_stack);
		return PARSE_SUCCESS;
	} else { //handling_fra如果大于1，说明有不知一个.这他喵就不是一个数
		push(&num_stack,0.0);
		printf(Error_Format);
		return PARSE_ERROR;
	}
}
